/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package avl.sv.server.slides;

import avl.sv.server.KeyValueRef;
import avl.sv.server.sources.SlideSourceKVStore;
import avl.sv.shared.SlideReference;
import avl.sv.shared.SlideSet;
import avl.sv.shared.SlideSets;
import avl.sv.shared.XML.SlideSetsXML_Writer;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.DatatypeConverter;
import javax.xml.ws.WebServiceContext;
import oracle.kv.Depth;
import oracle.kv.Direction;
import oracle.kv.KVStore;
import oracle.kv.Key;
import oracle.kv.KeyValueVersion;
import oracle.kv.Value;
import oracle.kv.ValueVersion;

/**
 *
 * @author benbryan
 */
@WebService(serviceName = "Slides")
public class Slides {
    
    @Resource
    private WebServiceContext wsContext;
    
    private UploadAuthManager uploadAuthManager = UploadAuthManager.getInstance() ;
    
    static {
        KeyValueRef.getRef();
    } 
    
    private SlideSourceKVStore findSlide(String slideSetName, String slideName){
        ArrayList<String> major = new ArrayList<>();
        major.add("slide");
        major.add(slideSetName);
        major.add(slideName);
        ValueVersion vv = KeyValueRef.getRef().get(Key.createKey(major));
        byte[] hash = vv.getValue().getValue();
        return new SlideSourceKVStore(new SlideReference(slideSetName, slideName, hash), KeyValueRef.getRef());
    }
    
    @WebMethod(operationName = "getImageInfo")
    public String[] getImageInfo( @WebParam(name = "slideSetName") final String slideSetName, @WebParam(name = "slideName") final String slideName) {
        SlideSourceKVStore slide = findSlide(slideSetName, slideName);   
        if (slide == null){
            return null;
        }
        return slide.getPropertiesAsStrings();
    }

    @WebMethod(operationName = "getTile")
    public byte[] getTile(  @WebParam(name = "slideSetName") final String slideSetName, 
                            @WebParam(name = "slideName") final String slideName, 
                            @WebParam(name = "directoryIndex") final int directoryIndex, 
                            @WebParam(name = "tileX") final int tileX, 
                            @WebParam(name = "tileY") final int tileY)  {
        SlideReference slideReference = new SlideReference(slideSetName, slideName, null);
        byte out[] = SlideSourceKVStore.getTileAsByteArray(KeyValueRef.getRef(), slideReference, directoryIndex, tileX, tileY);
        return out;
    }

    @WebMethod(operationName = "getJpegTables")
    public byte[] getJpegTables(    @WebParam(name = "slideSetName") final String slideSetName, 
                                    @WebParam(name = "slideName") final String slideName, 
                                    @WebParam(name = "directoryIndex") final int directoryIndex)  {
        SlideSourceKVStore slide = findSlide(slideSetName, slideName);  
        if (slide == null){
            return null;
        }
        byte[] out = slide.getJpegTables(directoryIndex);
        return out;
    }
        
    @WebMethod(operationName = "getSlideSets")
    public String getSlideSets() {
        if (KeyValueRef.getRef() == null){
            return "error: sorry, the slide setver is having issues";
        }
        SlideSets dataSets = new SlideSets();
        ArrayList<String> major = new ArrayList<>();
        major.add("slide");
        Iterator<Key> datasetIter = KeyValueRef.getRef().storeKeysIterator(Direction.UNORDERED, 0, Key.createKey(major), null, Depth.CHILDREN_ONLY);
        while (datasetIter.hasNext()) {
            Key datasetKey = datasetIter.next();
            Iterator<KeyValueVersion> slideIter = KeyValueRef.getRef().storeIterator(Direction.UNORDERED, 0, datasetKey, null, Depth.CHILDREN_ONLY);
            while (slideIter.hasNext()){
                KeyValueVersion vv = slideIter.next();
                java.util.List<String> path = vv.getKey().getMajorPath();
                String slideName = path.get(path.size()-1);
                String slideSetName = path.get(path.size()-2);
                byte slideHash[] = vv.getValue().getValue();
                               
                SlideReference sn = new SlideReference(slideSetName, slideName, slideHash);
                
                boolean added = false;
                for (SlideSet slideSet:dataSets.getSlideSets()){
                    if (slideSetName.equals(slideSet.getSlideSetName())){
                        slideSet.add(sn);
                        added = true;
                        break;
                    }
                }
                if (added == false){
                    SlideSet slideSet = new SlideSet(slideSetName);
                    slideSet.add(sn);
                    dataSets.add(slideSet);
                }
            }
        }     
        String xml = SlideSetsXML_Writer.getXMLString(dataSets);
        return xml;
    }    
      
    @WebMethod(operationName = "slidePostInit")
    public String slidePostInit( @WebParam(name = "slideSetName") final String slideSetName, 
                                  @WebParam(name = "fileName") final String fileName, 
                                  @WebParam(name = "newImageID") final byte[] newImageID) {
        KVStore kvstore = KeyValueRef.getRef();
        final ArrayList<String> major = new ArrayList<>();
        major.add("slide");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(slideSetName);
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(fileName);
        ValueVersion vv = kvstore.get(Key.createKey(major));
        if (vv != null){
            byte currentImageID[] = vv.getValue().getValue();
            boolean mismatch = currentImageID.length != newImageID.length;
            for (int i = 0; i < currentImageID.length; i++){
                mismatch |= (currentImageID[i] != newImageID[i]);
                if (mismatch){
                    break;
                }
            }
            if (mismatch){
                return "error: slide dataset\\name combination already exists";
            } else {
                return "error: slide already exists on server under dataset\\name combination";
            }
        } else {
            kvstore.put(Key.createKey(major), Value.createValue(newImageID));
            UploadAuth auth = new UploadAuth(slideSetName, fileName);
            uploadAuthManager.putAuth(auth.uploadToken, auth);
            return auth.uploadToken;
        }
    }

    @WebMethod(operationName = "slidePostSetupDirectory")
    public String slidePostSetupDirectory(  @WebParam(name = "uploadToken") final String uploadToken, 
                                            @WebParam(name = "directoryIdx") final int directoryIdx, 
                                            @WebParam(name = "propertiesXML") final String propertiesXML) {
        UploadAuth auth = uploadAuthManager.getAuth(uploadToken);
        if ((auth == null) || (auth.isExpired())){
            return "error: no authorization or upload session expired";
        }
        try {
            KVStore kvstore = KeyValueRef.getRef();
            final ArrayList<String> major = new ArrayList<>();
            major.add("slide");
            major.add(auth.slideSetName);
            major.add(auth.fileName);

            ArrayList<String> minor = new ArrayList<>();
            minor.add(String.valueOf(directoryIdx));
            kvstore.put(Key.createKey(major, minor), Value.EMPTY_VALUE);
            minor.add("properties");
            kvstore.put(Key.createKey(major, minor), Value.EMPTY_VALUE);
            minor.add("This string just allocates space");

            Properties props = new Properties();
            props.loadFromXML(new BufferedInputStream(new ByteArrayInputStream( propertiesXML.getBytes())));
            for (String propName:props.stringPropertyNames()){
                minor.set(2, propName);
                String propData = props.getProperty(propName);
                if ("JpegTables".equals(propName)){
                    byte b[] = DatatypeConverter.parseBase64Binary(propData);
                    kvstore.put(Key.createKey(major, minor), Value.createValue(b));
                } else {
                    kvstore.put(Key.createKey(major, minor), Value.createValue(propData.getBytes()));
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(Slides.class.getName()).log(Level.SEVERE, null, ex);
            return "error: somthing went wrong during authroized access";
        }
        return "Directory setup";
    }
    
    @WebMethod(operationName = "slidePostTile")
    public String slidePostTile( @WebParam(name = "uploadToken") final String uploadToken, 
                                 @WebParam(name = "directoryIdx") final int directoryIdx, 
                                 @WebParam(name = "tileX") final int tileX, 
                                 @WebParam(name = "tileY") final int tileY, 
                                 @WebParam(name = "tile") final byte tile[]) {
        UploadAuth auth = uploadAuthManager.getAuth(uploadToken);
        if ((auth == null) || (auth.isExpired())){
            return "error: no authorization or upload session expired";
        }
        if ((tileX < 0) || (tileY < 0)){
            return "error: tile idx out of bounds";
        }
        if (directoryIdx < 0){
            return "error: directory idx out of bounds";
        }
        if ((tile == null) || (tile.length == 0)){
            return "error: tile is null";
        }
        
        final ArrayList<String> major = new ArrayList<>();
        major.add("slide");
        major.add(auth.slideSetName);
        major.add(auth.fileName);

        ArrayList<String> minor = new ArrayList<>();
        minor.add(String.valueOf(directoryIdx));
        minor.add(String.valueOf("tiles"));
        minor.add(String.valueOf(tileX));
        minor.add(String.valueOf(tileY));
        KeyValueRef.getRef().put(Key.createKey(major, minor), Value.createValue(tile));

        return "Posted";
    }
    
    @WebMethod(operationName = "slidePostFinalize")
    public String slidePostFinalize( @WebParam(name = "uploadToken") final String uploadToken, 
                                     @WebParam(name = "deleteSlide") final boolean deleteSlide){
        UploadAuth auth = uploadAuthManager.removeAuth(uploadToken);
        if (auth == null){
            return "error: somthing failed during post, slide should not exist on server";
        } else if (deleteSlide){
            deleteSlide(auth.slideSetName, auth.fileName);
            return "slide deleted";            
        } else {
            return "successfully added the slide to the server";
        }
    }
    
    @WebMethod(operationName = "deleteSlide")
    public String deleteSlide( @WebParam(name = "slideSetName") final String slideSetName, 
                               @WebParam(name = "slideName") final String slideName){
        KVStore kvstore = KeyValueRef.getRef();
        final ArrayList<String> major = new ArrayList<>();
        major.add("slide");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(slideSetName);
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(slideName);
        Key key = Key.createKey(major);
        Iterator<Key> iter = kvstore.storeKeysIterator(Direction.UNORDERED, 0, key, null, Depth.PARENT_AND_DESCENDANTS);
        while (iter.hasNext()){
            key = iter.next();
            kvstore.multiDelete(key, null, Depth.PARENT_AND_DESCENDANTS);
        } 
        return "slide " + slideName+ " in dataset " + slideSetName + " was deleted from the server";
    }

    @WebMethod(operationName = "getFeature")
    public String getFeature(@WebParam(name = "slideSetName") final String slideSetName, 
                            @WebParam(name = "slideName") final String slideName,
                            @WebParam(name = "tileDim") final int tileDim,
                            @WebParam(name = "featureGeneratorName") final String featureGeneratorName,
                            @WebParam(name = "featureName") final String featureName) {

        return null;
    }
    
   
}

